#!/bin/bash

flagPara=$1
contPara=$2
fileIn=
fileOut=
baseDir="./"

package=
interface=
imports=
returns=
funcs=
params=
members=
#原有变量
membersOriginals=

VERSION=1.1.0

function usage(){
    echo -e "usage: $(basename $0) -d dir | -f file | -h | -v"
    echo -e "params:"
    echo -e "\t-d dir  #file path"
    echo -e "\t-f file #file name"
    echo -e "\t-h      #usage"
    echo -e "\t-v      #version" 
}

##tools
function write(){
    local info=$1
    echo -e "${info}" >> $fileOut
}

function stringFirst2Upper(){
    local str=$1
    local first=${str:0:1}
    local others=${str:1}

    first=`echo $first | tr 'a-z' 'A-Z'`
    echo ${first}${others}
}

function date_echo(){
    echo -e "[`date +'%F %T'`] $@"
}

function checkRes(){
    if [ $? -ne 0 ];then
         $@
    fi
    return 0
}

function logFatal(){
    local mgs="FATAL: $(basename $0):(${FUNCNAME[@]:1}) [$1]"
    date_echo "\033[31;31;1m'$mgs'\033[0m"
    exit 1
}

function logWarning(){
     local mgs="WARNING: $(basename $0):(${FUNCNAME[@]:1}) [$1]"
     date_echo  "\033[33;33;1m'$mgs'\033[0m"
}

function logNotice(){
     local mgs_head="NOTICE: $(basename $0):(${FUNCNAME[@]:1}) ["
     date_echo  $mgs_head"\033[34;34;1m'$1'\033[0m"]
}

##implement
function parseFile(){
    test -e $fileIn || logFatal "file not exists![file:$fileIn]"
    logNotice "############### parse file [$fileIn] ###############"
    vi +':w ++ff=unix' +':q' $fileIn  ##win to linux

    local line=
    while read line
    do
        line=`echo "$line" | awk '{sub(/^ */,"");sub(/ *$/,"")}1'`
        test "x${line}" == "x" && continue
        
        if [[ "$line" =~ ^package.* ]];then 
            package="$line" 
            continue
        elif [[ "$line" =~ ^import.* ]];then
            line=`echo "$line" | awk '{print $2}'`
            imports="${imports}%${line}"
            continue
        elif [[ "$line" =~ ^public.?interface.* ]];then
            interface=`echo "$line" | awk -F' ' '{print $3}'`
            interface=${interface%%<*}
            continue
        fi
       
        test "${line:0-1:1}" != ";" && continue
        if [[ "$line" =~ " = " ]];then
            if ! [[ "$line" =~ "@" ]];then
                membersOriginals="$membersOriginals%$line"
                continue
            fi
        fi
      
        line=${line%)*}
        parseFuncDefine "$line"               
    done < $fileIn

    membersRemoveDup
}

function parseFuncDefine(){
    local func=$1
    local start=
    local end=

    #start=`echo $func | awk -F'(' '{print $1}'`
    #end=`echo $func | awk -F'(' '{print $2}'`
    start=${func%%(*}
    end=${func#*(}
    end=${end//.../}
    end=${end//@NotNull/}
    end=${end//@Size(min=1, max=20)/}
    end=${end//final /}
    end=${end//Valid /}
    end=${end//(groups = \{UpdateBaseDataValidation.class\})/}

    local r= f=
    start=${start//, /,}     
    read r f <<<$(awk '{print $(NF-1)" "$NF}' <<<"$start")
    funcs="$funcs%$func)"
    returns="$returns $r"
    members="$members ${f}ReturnValue"
    
    local p=`parseParam "$end"`
    params="$params $p"
}

function parseParam(){
    local info=$1
    info=${info//, /,}
    OLD_IFS="$IFS"
    IFS=","
    local arrInfos=($info)
    IFS="$OLD_IFS"
    local i=0
    local arrItem=
    local res=
    local flag=0
    for((;i<${#arrInfos[@]};i++))
    do
        arrItem=(${arrInfos[$i]})
        if [ ${#arrItem[@]} -eq 2 ];then
            if [ $flag -eq 0 ];then
                res="$res%${arrItem[0]}"
            elif [ $flag -eq 1 ];then
                flag=0
                res="$res${arrItem[0]}"
            fi
        elif [ ${#arrItem[@]} -eq 1 ];then 
            res="$res%${arrItem[0]},"
            flag=1
        fi
    done
    
    test "x$res" == "x" && res="void"
    echo $res 
}

#去除重复函数名带来的成员变量重名问题,解决方式为加上参数类型
function membersRemoveDup(){
    local arrMembers=($members)
    local arrParams=($params)
    local i=0 j=0
    local p= 
    local flag=0
    local set len=${#arrMembers[@]}

    for((;i<$len-1;i++))
    do
        for((j=$i+1;j<$len;j++))
        do
            test ${arrMembers[$i]} != ${arrMembers[$j]} && continue
            p=`adjustType "${arrParams[$j]}"`
            flag=1
            test "x$p" == "x" || arrMembers[$j]="${arrMembers[$j]}P${p}"
        done
        
        test $flag -eq 0 && continue
        p=`adjustType "${arrParams[$i]}"`     
        flag=0
        test "x$p" == "x" || arrMembers[$i]="${arrMembers[$i]}P${p}"
    done

    members=""
    for((i=0;i<$len;i++))
    do
        members="$members ${arrMembers[$i]}"
    done
}

function adjustType(){
    local t=$1
    OLD_IFS="$IFS"
    IFS="%"
    local types=($t)
    IFS="$OLD_IFS"
    local res=
    local i=0
    local info=

    for((;i<${#types[@]};i++))
    do
        test "x${types[$i]}" == "x" && continue
        case "${types[$i]}" in 
            "void")
                ;;
            "int")
                info="Int";;
            "String")
                info="Str";;
            "byte")
                info="Byte";;
            "short")
                info="Short";;
            "long")
                info="Long";;
            "float")
                info="Float";;
            "double")
                info="Doub";;
            "boolean")
                info="Bool";;
            "char")
                info="Char";;
            *)
                if [[ ${types[$i]} =~ ^Map.* ]];then
                    info="Map"
                elif [[ ${types[$i]} =~ ^List.* ]];then
                    info="List"
                elif [[ ${types[$i]} =~ ^Set.* ]];then
                    info="Set"
                elif [[ ${types[$i]} =~ ^Collection.* ]];then
                    info="Coll"
                else
                    info=`echo "${types[$i]}" | tr 'A-Z' 'a-z'`    
                    info=${info%%[*} 
                    info=`stringFirst2Upper $info`
                fi
                ;; 
        esac     
        res="${res}${info}"  
    done
    echo "$res"
}

function makeImport(){
    logNotice "############### make import ###############"
    OLD_IFS="$IFS"
    IFS="%"
    local arrImports=($imports)
    local i=1
    
    package=${package%%;*}
    write "import ${package:8}.$interface;"
    write "import java.util.*;"
    for((;i<${#arrImports[@]};i++))
    do
        write "import ${arrImports[$i]}"
    done    
    IFS="$OLD_IFS"
    write ""
}

function makeMembers(){
    logNotice "############### make members ###############"
    local arrReturns=($returns)
    local arrMembers=($members)
    local i=0 j=1

    OLD_IFS="$IFS"
    IFS="%"
    local arrMembersOriginals=($membersOriginals)
    if [ ${#arrMembersOriginals[@]} -ne 0 ];then
        echo -e "" >> $fileOut
        for((;j<${#arrMembersOriginals[@]};j++)) 
        do
            write "\t${arrMembersOriginals[$j]}"
        done 
        write "" 
    fi
    IFS="$OLD_IFS"

    for((;i<${#arrMembers[@]};i++))
    do
        test ${arrReturns[$i]} == "void" && continue
        write "\tprivate ${arrReturns[$i]} ${arrMembers[$i]};"
    done
    write ""
}

function makeConstructor(){
    logNotice "############### init members ###############"
    local arrReturns=($returns)
    local arrMembers=($members)
    local info=
    local i=0

    write "\tpublic Mock$interface(){" 
    for((;i<${#arrReturns[@]};i++))
    do
        case "${arrReturns[$i]}" in
            "void")
                continue;;
            "int")
                info=0;;
            "String")
                info="\"\"";;
            "byte")
                info=0;;
            "short")
                info=0;;
            "long")
                info="0L";;
            "float")
                info="0.0f";;
            "double")
                info="0.0d";;
            "boolean")
                info="false";;
            "char")
                info=0;;
            *)
                #if [[ ${arrReturns[$i]} =~ ^Map.* ]];then
                #    info="new Hash${arrReturns[$i]}()"
                #elif [[ ${arrReturns[$i]} =~ ^Set.* ]];then
                #    info="new Hash${arrReturns[$i]}()"
                #elif [[ ${arrReturns[$i]} =~ ^List.* ]];then
                #    info="new Array${arrReturns[$i]}()"
                if [[ ${arrReturns[$i]} =~ ^BigDecimal.* ]];then
                    info="new ${arrReturns[$i]}(0)"
                elif [[ ${arrReturns[$i]} =~ ^Integer.* ]];then
                    info="new ${arrReturns[$i]}(0)"
                elif [[ ${arrReturns[$i]} =~ ^Long.* ]];then
                    info="new ${arrReturns[$i]}(0)"
                #elif [[ ${arrReturns[$i]} == "UpdateOrderTrainEnum" ]];then
                #    info="${arrReturns[$i]}.success"
                #elif [[ ${arrReturns[$i]} == "BindResult" ]];then
                #    info="${arrReturns[$i]}.send_succ"
                #elif [[ ${arrReturns[$i]} =~ ^TradeResult.* ]];then #abstract
                #    continue
                else
                    continue
                    #info="new ${arrReturns[$i]}()"
                fi
                ;;
        esac
        write "\t\t${arrMembers[$i]} = $info;"
    done
    write "\t}"
    write "" 
}

function makeMockFunc(){
    logNotice "############### overwrite funcs ###############"
    local arrMembers=($members)
    local arrReturns=($returns)
    local info= 

    OLD_IFS="$IFS" 
    IFS="%"
    funcs=${funcs:1}
    local arrFuncs=($funcs)
    IFS="$OLD_IFS"
    local i=0
    for((;i<${#arrFuncs[@]};i++))
    do
        info=
        test "${arrReturns[$i]}" != "void" && info="return instance.${arrMembers[$i]};"
        if [[ "${arrFuncs[$i]}" =~ ^public.* ]];then
            write "\t${arrFuncs[$i]}{$info}"
        else
            write "\tpublic ${arrFuncs[$i]}{$info}"
        fi
        write ""
    done 
    write ""
}

function makeSetFunc(){
    logNotice "############### members setfuncs ###############"
    local arrReturns=($returns)
    local arrMembers=($members)
    local func=

    local i=0
    for((;i<${#arrMembers[@]};i++))
    do
        test ${arrReturns[$i]} == "void" && continue
        func=`stringFirst2Upper ${arrMembers[$i]}`
        write "\tpublic static void set${func}(${arrReturns[$i]} p){instance.${arrMembers[$i]} = p;}"
        write ""
    done
}

function makeMockFile(){
    local path=mock/$baseDir
    test -d $path || mkdir -p $path
    fileOut=$path/"Mock"${interface}.java

    cat /dev/null > $fileOut
    #echo $package >> $fileOut
    local pack=${baseDir//\//.}
    pack=${pack//-/_}
    write "package com.qunar.train.mock${pack:1};"
    echo "" >> $fileOut

    makeImport
   
    write "public class Mock$interface implements $interface{" 
    write "\tpublic static Mock$interface instance = new Mock$interface();"
    write ""
    makeMembers
    makeConstructor
    makeMockFunc
    makeSetFunc
    write "}"
}

function execute(){
    logNotice "############### mock start [$fileIn] ################"
    local info=`grep "public.*interface" $fileIn`
    test "x$info" == "x" && logWarning "not interface File: $fileIn" && return
     
    parseFile
    makeMockFile
}

function clearUp(){
    package=
    interface=
    imports=
    returns=
    funcs=
    params=
    members=
    membersOriginals=
}

function run(){
    local files=
    local file=
    if [ "x$flagPara" == "x-d" ];then
        #files=`ls $contPara | grep ".java"`
        files=`find $contPara -name "*.java"`
        for file in $files
        do
            #baseDir=$contPara
            #fileIn="${baseDir}/${file}"
            baseDir=${file%/*}
            fileIn=$file
            execute
            clearUp
        done
    elif [ "x$flagPara" == "x-f" ];then
        fileIn="$contPara"
        execute
    elif [ "x$flagPara" == "x-h" ];then
        usage
    elif [ "x$flagPara" == "x-v" ];then
        echo $VERSION
    else
        echo "wrong params!"
        usage
    fi 
}

run

